package cn.yjh.server.net;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.locks.LockSupport;


/**
 * @author yW0041229
 * @since 2021/12/16 10:42
 */
public class SlaveReactor extends DefaultDispatcher implements Runnable{

    private final Selector selector;

    // 注册开关表示，为什么要加这么个东西，可以参考Acceptor设置这个值那里的描述
    private boolean disableSelect = false;

    private String status = "wait";

    // 从 Reactor 编号
    private int num;

    SlaveReactor(Selector selector, int num) {
        this.selector = selector;
        this.num = num;
    }

    @Override
    public void run() {

        String blockType = Optional.ofNullable(System.getProperty("server.non-blocking")).orElseGet(()-> "false");

        while (!Thread.interrupted()) {

            // 当没有请求的时候，将当前 reactor 阻塞住，防止 cpu 不停运行，导致 cpu 负载过高
            if("true".equals(blockType) && "wait".equals(status)){
                LockSupport.park();
            }

            /**
             * 注意一个 selector 在执行 select() 方法阻塞时是无法注册新事件的，
             * 所以这里定义一个标识，用于控制是否执行 select() 方法，当需要注册新事件时，
             * 首先将 disableSelect 开关打开，然后调用 selector.weakup() 方法，
             * 让阻塞住的 select() 方法立即返回，结束阻塞，然后再注册新事件，最后再关闭开关
             */
            if (!disableSelect) {
                try {
                    if (selector.select() == 0) {
                        continue;
                    }
                    Set selectedKeys = selector.selectedKeys();
                    Iterator it = selectedKeys.iterator();
                    while (it.hasNext()) {
                        SelectionKey sk = (SelectionKey)it.next();
                        if(sk.attachment() != null){
                            this.dispatch(sk);
                        }
                        it.remove();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    void setDisableSelect(boolean disableSelect) {
        this.disableSelect = disableSelect;
    }

    void setStatus(String status) {
        this.status = status;
    }
}
